package com.winlator.container;

import android.os.Environment;
import android.util.Log;

import com.winlator.box86_64.Box86_64Preset;
import com.winlator.core.DefaultVersion;
import com.winlator.core.envvars.EnvVars;
import com.winlator.core.FileUtils;
import com.winlator.core.KeyValueSet;
import com.winlator.core.WineInfo;
import com.winlator.core.WineThemeManager;
import com.winlator.fexcore.FEXCorePreset;
import com.winlator.winhandler.WinHandler;
import com.winlator.xenvironment.ImageFs;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.util.Iterator;

public class Container {
    public enum XrControllerMapping {
        BUTTON_A, BUTTON_B, BUTTON_X, BUTTON_Y, BUTTON_GRIP, BUTTON_TRIGGER,
        THUMBSTICK_UP, THUMBSTICK_DOWN, THUMBSTICK_LEFT, THUMBSTICK_RIGHT
    }

    public static final String DEFAULT_ENV_VARS = "WRAPPER_MAX_IMAGE_COUNT=0 ZINK_DESCRIPTORS=lazy ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1 MESA_VK_WSI_PRESENT_MODE=mailbox TU_DEBUG=noconform DXVK_FRAME_RATE=60 PULSE_LATENCY_MSEC=144";
    public static final String DEFAULT_SCREEN_SIZE = "1280x720";
    public static final String DEFAULT_GRAPHICS_DRIVER = DefaultVersion.DEFAULT_GRAPHICS_DRIVER;
    public static final String DEFAULT_AUDIO_DRIVER = "pulseaudio";
    public static final String DEFAULT_EMULATOR = "FEXCore";
    public static final String DEFAULT_DXWRAPPER = "dxvk";
    public static final String DEFAULT_DDRAWRAPPER = "none";
    public static final String DEFAULT_DXWRAPPERCONFIG = "version=" + DefaultVersion.DXVK + ",framerate=0,maxDeviceMemory=0,async=" + DefaultVersion.ASYNC + ",asyncCache=" + DefaultVersion.ASYNC_CACHE + ",vkd3dVersion=" + DefaultVersion.VKD3D + ",vkd3dLevel=12_1" + ",ddrawrapper=" + Container.DEFAULT_DDRAWRAPPER + ",csmt=3" + ",gpuName=NVIDIA GeForce GTX 480" + ",videoMemorySize=2048" + ",strict_shader_math=1" + ",OffscreenRenderingMode=fbo" + ",renderer=gl";;
    public static final String DEFAULT_GRAPHICSDRIVERCONFIG = "vulkanVersion=1.3" + ",version=" + DefaultVersion.WRAPPER + ",blacklistedExtensions=" + ",maxDeviceMemory=0" + ",presentMode=mailbox" + ",syncFrame=0" + ",disablePresentWait=0" + ",resourceType=auto" + ",bcnEmulation=auto" + ",bcnEmulationType=software" + ",bcnEmulationCache=0";
    public static final String DEFAULT_WINCOMPONENTS = "direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0,vcrun2010=1,wmdecoder=1,opengl=0";
    public static final String FALLBACK_WINCOMPONENTS = "direct3d=1,directsound=1,directmusic=1,directshow=1,directplay=1,vcrun2010=1,wmdecoder=1,opengl=0";
    public static final String[] MEDIACONV_ENV_VARS = {
            "MEDIACONV_AUDIO_DUMP_FILE=/data/data/app.gamenative/files/imagefs/home/xuser/audio.dmp",
            "MEDIACONV_VIDEO_DUMP_FILE=/data/data/app.gamenative/files/imagefs/home/xuser/video.dmp",
            "MEDIACONV_VIDEO_TRANSCODED_FILE=/data/data/app.gamenative/files/imagefs/home/xuser/transcoded.mkv",
            "MEDIACONV_AUDIO_TRANSCODED_FILE=/data/data/app.gamenative/files/imagefs/home/xuser/transcoded.wav",
            "MEDIACONV_BLANK_AUDIO_FILE=/data/data/app.gamenative/files/imagefs/home/xuser/blank.wav",
            "MEDIACONV_BLANK_VIDEO_FILE=/data/data/app.gamenative/files/imagefs/home/xuser/blank.mkv",
    };
    public static final String DEFAULT_DRIVES = "D:"+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"E:/data/data/app.gamenative/storage";
    public static final String DEFAULT_VARIANT = DefaultVersion.VARIANT;
    public static final String DEFAULT_WINE_VERSION = DefaultVersion.WINE_VERSION;
    public static final byte STARTUP_SELECTION_NORMAL = 0;
    public static final byte STARTUP_SELECTION_ESSENTIAL = 1;
    public static final byte STARTUP_SELECTION_AGGRESSIVE = 2;
    public static final String STEAM_TYPE_NORMAL = "normal";
    public static final String STEAM_TYPE_LIGHT = "light";
    public static final String STEAM_TYPE_ULTRALIGHT = "ultralight";
    public static final String GLIBC = "glibc";
    public static final String BIONIC = "bionic";
    public static final byte MAX_DRIVE_LETTERS = 8;
    public final String id;
    private String name;
    private String screenSize = DEFAULT_SCREEN_SIZE;
    private String envVars = DEFAULT_ENV_VARS;
    private String graphicsDriver = DEFAULT_GRAPHICS_DRIVER;
    private String dxwrapper = DEFAULT_DXWRAPPER;
    private String dxwrapperConfig = DEFAULT_DXWRAPPERCONFIG;
    private String graphicsDriverConfig = DEFAULT_GRAPHICSDRIVERCONFIG;
    private String wincomponents = DEFAULT_WINCOMPONENTS;
    private String audioDriver = DEFAULT_AUDIO_DRIVER;
    private String drives = DEFAULT_DRIVES;
    private String wineVersion = WineInfo.MAIN_WINE_VERSION.identifier();
    private boolean showFPS;
    private boolean launchRealSteam;
    private boolean allowSteamUpdates;
    private boolean wow64Mode = true;
    private boolean needsUnpacking = true;
    private byte startupSelection = STARTUP_SELECTION_AGGRESSIVE;
    private String cpuList;
    private String cpuListWoW64;
    private String desktopTheme = WineThemeManager.DEFAULT_DESKTOP_THEME;
    private String box86Version = DefaultVersion.BOX86;
    private String box64Version = DefaultVersion.BOX64;
    private String box86Preset = Box86_64Preset.PERFORMANCE;
    private String box64Preset = Box86_64Preset.PERFORMANCE;
    private String fexcoreVersion = DefaultVersion.FEXCORE;
    private String fexcorePreset = FEXCorePreset.INTERMEDIATE;
    private String emulator = DEFAULT_EMULATOR;
    private File rootDir;
    private String installPath = "";
    private JSONObject extraData;
    private JSONObject sessionMetadata;
    private int rcfileId = 0;
    private String midiSoundFont = "";
    private int inputType = WinHandler.PreferredInputApi.BOTH.ordinal();
    private String lc_all = "en_US.utf8";
    private int primaryController = 1;
    private String controllerMapping = new String(new char[XrControllerMapping.values().length]);

    private String graphicsDriverVersion = "25.1.0"; // Default version or fallback

    private String execArgs = ""; // Default exec arguments
    private String executablePath = ""; // Executable path for container
    private boolean sdlControllerAPI;

    // Preferred game language for Goldberg force_language.txt
    private String language = "english";

    private ContainerManager containerManager;

    private byte dinputMapperType = 1;  // 1=standard, 2=XInput mapper
    // Disable external mouse input
    private boolean disableMouseInput = false;
    // Touchscreen mode
    private boolean touchscreenMode = false;
    // Prefer DRI3 WSI path
    private boolean useDRI3 = true;
    // Steam client type for selecting appropriate Box64 RC config: normal, light, ultralight
    private String steamType = DefaultVersion.STEAM_TYPE;

    // Emulate keyboard/mouse using controller: left stick=WASD, right stick=mouse
    private boolean emulateKeyboardMouse = false;
    // Serialized as JSON object: logical button name -> Binding enum name
    private JSONObject controllerEmulationBindings;
    private boolean gstreamerWorkaround = false;

    private boolean forceDlc = false;

    private boolean useLegacyDRM = false;

    private String containerVariant = DEFAULT_VARIANT;

    public String getGraphicsDriverVersion() {
        return graphicsDriverVersion;
    }

    public void setGraphicsDriverVersion(String graphicsDriverVersion) {
        this.graphicsDriverVersion = graphicsDriverVersion;
    }

    public String getSteamType() {
        return steamType;
    }

    public void setSteamType(String steamType) {
        String normalized = (steamType == null) ? "" : steamType.toLowerCase();
        switch (normalized) {
            case STEAM_TYPE_LIGHT:
                this.steamType = STEAM_TYPE_LIGHT;
                break;
            case STEAM_TYPE_ULTRALIGHT:
                this.steamType = STEAM_TYPE_ULTRALIGHT;
                break;
            default:
                this.steamType = STEAM_TYPE_NORMAL;
                break;
        }
    }

    public String getExecArgs() {
        return execArgs;
    }

    public void setExecArgs(String execArgs) {
        this.execArgs = execArgs != null ? execArgs : "";
    }

    public String getExecutablePath() {
        return executablePath;
    }

    public void setExecutablePath(String executablePath) {
        this.executablePath = executablePath != null ? executablePath : "";
    }

    public Container(String id) {
        this.id = id;
        this.name = "Container-"+id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getScreenSize() {
        return screenSize;
    }

    public void setScreenSize(String screenSize) {
        this.screenSize = screenSize;
    }

    public String getEnvVars() {
        return envVars;
    }

    public void setEnvVars(String envVars) {
        this.envVars = envVars != null ? envVars : "";
    }

    public String getGraphicsDriver() {
        return graphicsDriver;
    }

    public void setGraphicsDriver(String graphicsDriver) {
        this.graphicsDriver = graphicsDriver;
    }

    public String getDXWrapper() {
        return dxwrapper;
    }

    public void setDXWrapper(String dxwrapper) {
        this.dxwrapper = dxwrapper;
    }

    public String getGraphicsDriverConfig() {
        return this.graphicsDriverConfig;
    }

    public void setGraphicsDriverConfig(String graphicsDriverConfig) {
        this.graphicsDriverConfig = graphicsDriverConfig != null ? graphicsDriverConfig : "";
    }

    public String getDXWrapperConfig() {
        return dxwrapperConfig;
    }

    public void setDXWrapperConfig(String dxwrapperConfig) {
        this.dxwrapperConfig = dxwrapperConfig != null ? dxwrapperConfig : "";
    }

    public String getAudioDriver() {
        return audioDriver;
    }

    public void setAudioDriver(String audioDriver) {
        this.audioDriver = audioDriver;
    }

    public String getWinComponents() {
        return wincomponents;
    }

    public void setWinComponents(String wincomponents) {
        this.wincomponents = wincomponents;
    }

    public String getDrives() {
        return drives;
    }

    public void setDrives(String drives) {
        this.drives = drives;
    }

    public String getLC_ALL() {
        return lc_all;
    }

    public void setLC_ALL(String lc_all) {
        this.lc_all = lc_all;
    }

    public int getPrimaryController() {
        return primaryController;
    }

    public void setPrimaryController(int primaryController) {
        this.primaryController = primaryController;
    }

    public byte getControllerMapping(XrControllerMapping input) {
        return (byte) controllerMapping.charAt(input.ordinal());
    }

    public void setControllerMapping(String controllerMapping) {
        this.controllerMapping = controllerMapping;
    }

    public boolean isShowFPS() {
        return showFPS;
    }

    public void setShowFPS(boolean showFPS) {
        this.showFPS = showFPS;
    }

    public boolean isLaunchRealSteam() {
        return launchRealSteam;
    }

    public void setLaunchRealSteam(boolean launchRealSteam) {
        this.launchRealSteam = launchRealSteam;
    }

    public boolean isAllowSteamUpdates() {
        return allowSteamUpdates;
    }

    public void setAllowSteamUpdates(boolean allowSteamUpdates) {
        this.allowSteamUpdates = allowSteamUpdates;
    }

    public boolean isSdlControllerAPI() {
        return sdlControllerAPI;
    }

    public void setSdlControllerAPI(boolean sdlControllerAPI) {
        this.sdlControllerAPI = sdlControllerAPI;
    }

    public String getLanguage() {
        return language != null ? language : "english";
    }

    public void setLanguage(String language) {
        this.language = (language != null && !language.isEmpty()) ? language : "english";
    }

    public boolean isWoW64Mode() {
        return wow64Mode;
    }

    public void setWoW64Mode(boolean wow64Mode) {
        this.wow64Mode = wow64Mode;
    }

    public boolean isNeedsUnpacking() {
        return needsUnpacking;
    }

    public void setNeedsUnpacking(boolean needsUnpacking) {
        this.needsUnpacking = needsUnpacking;
    }

    public byte getStartupSelection() {
        return startupSelection;
    }

    public void setStartupSelection(byte startupSelection) {
        this.startupSelection = startupSelection;
    }

    public String getCPUList() {
        return getCPUList(true);
    }

    public String getCPUList(boolean allowFallback) {
        return cpuList != null ? cpuList : (allowFallback ? getFallbackCPUList() : null);
    }

    public void setCPUList(String cpuList) {
        this.cpuList = cpuList != null && !cpuList.isEmpty() ? cpuList : null;
    }

    public String getCPUListWoW64() {
        return getCPUListWoW64(true);
    }

    public String getCPUListWoW64(boolean allowFallback) {
        return cpuListWoW64 != null ? cpuListWoW64 : (allowFallback ? getFallbackCPUListWoW64() : null);
    }

    public void setCPUListWoW64(String cpuListWoW64) {
        this.cpuListWoW64 = cpuListWoW64 != null && !cpuListWoW64.isEmpty() ? cpuListWoW64 : null;
    }

    public String getBox86Version() { return box86Version; }

    public void setBox86Version(String box86Version) { this.box86Version = box86Version; }

    public String getBox64Version() { return box64Version; }

    public void setBox64Version(String box64Version) { this.box64Version = box64Version; }

    public void setEmulator(String emulator) {
        this.emulator = emulator;
    }

    public String getEmulator() {
        return this.emulator;
    }

    public String getBox86Preset() {
        return box86Preset;
    }

    public void setBox86Preset(String box86Preset) {
        this.box86Preset = box86Preset;
    }

    public String getBox64Preset() {
        return box64Preset;
    }

    public void setBox64Preset(String box64Preset) {
        this.box64Preset = box64Preset;
    }

    public String getFEXCoreVersion() { return this.fexcoreVersion; }

    public void setFEXCoreVersion(String version) { this.fexcoreVersion = version; }

    public void setFEXCorePreset(String preset) { this.fexcorePreset = preset; }

    public String getFEXCorePreset() { return fexcorePreset; }

    public File getRootDir() {
        return rootDir;
    }

    public void setRootDir(File rootDir) {
        this.rootDir = rootDir;
    }

    public String getInstallPath() {
        return installPath;
    }

    public void setInstallPath(String installPath) {
        this.installPath = installPath != null ? installPath : "";
    }

    public void setExtraData(JSONObject extraData) {
        this.extraData = extraData;
    }

    public boolean isGstreamerWorkaround() { // Add this getter
        return this.gstreamerWorkaround;
    }

    public void setGstreamerWorkaround(boolean gstreamerWorkaround) { // Add this setter
        this.gstreamerWorkaround = gstreamerWorkaround;
    }

    public void setContainerVariant(String variant) {
        this.containerVariant = variant;
    }

    public String getContainerVariant() {
        return this.containerVariant;
    }

    public String getExtra(String name) {
        return getExtra(name, "");
    }

    public String getExtra(String name, String fallback) {
        try {
            return extraData != null && extraData.has(name) ? extraData.getString(name) : fallback;
        }
        catch (JSONException e) {
            return fallback;
        }
    }

    public void putExtra(String name, Object value) {
        if (extraData == null) extraData = new JSONObject();
        try {
            if (value != null) {
                extraData.put(name, value);
            }
            else extraData.remove(name);
        }
        catch (JSONException e) {
            Log.e("Container", "Failed to put extra: " + e);
        }
    }

    public String getSessionMetadata(String name) {
        return getSessionMetadata(name, "");
    }

    public String getSessionMetadata(String name, String fallback) {
        try {
            return sessionMetadata != null && sessionMetadata.has(name) ? sessionMetadata.getString(name) : fallback;
        }
        catch (JSONException e) {
            return fallback;
        }
    }

    public void putSessionMetadata(String name, Object value) {
        if (sessionMetadata == null) sessionMetadata = new JSONObject();
        try {
            if (value != null) {
                sessionMetadata.put(name, value);
            }
            else sessionMetadata.remove(name);
        }
        catch (JSONException e) {
            Log.e("Container", "Failed to put session metadata: " + e);
        }
    }

    public void clearSessionMetadata() {
        sessionMetadata = null;
    }

    public String getWineVersion() {
        return wineVersion;
    }

    public void setWineVersion(String wineVersion) {
        this.wineVersion = wineVersion;
    }

    public File getConfigFile() {
        return new File(rootDir, ".container");
    }

    public File getDesktopDir() {
        return new File(rootDir, ".wine/drive_c/users/"+ImageFs.USER+"/Desktop/");
    }

    public File getStartMenuDir() {
        return new File(rootDir, ".wine/drive_c/ProgramData/Microsoft/Windows/Start Menu/");
    }

    public File getIconsDir(int size) {
        return new File(rootDir, ".local/share/icons/hicolor/"+size+"x"+size+"/apps/");
    }

    public String getDesktopTheme() {
        return desktopTheme;
    }

    public void setDesktopTheme(String desktopTheme) {
        this.desktopTheme = desktopTheme;
    }

    public int getRCFileId() {
        return rcfileId;
    }

    public void setRcfileId(int id) {
        rcfileId = id;
    }

    public String getMIDISoundFont() {
        return midiSoundFont;
    }

    public void setMidiSoundFont(String fileName) {
        midiSoundFont = fileName;
    }

    public int getInputType() {
        return inputType;
    }

    public void setInputType(int inputType) {
        this.inputType = inputType;
    }

    /**
     * Gets the DirectInput mapper type: 1=standard, 2=XInput mapper
     */
    public byte getDinputMapperType() {
        return dinputMapperType;
    }

    /**
     * Sets the DirectInput mapper type: 1=standard, 2=XInput mapper
     */
    public void setDinputMapperType(byte dinputMapperType) {
        this.dinputMapperType = dinputMapperType;
    }

    public Iterable<String[]> drivesIterator() {
        return drivesIterator(drives);
    }

    public static char getNextAvailableDriveLetter(String drives) throws Exception {
        char drive = 'A';
        while (drives.contains(drive + ":")) {
            drive += 1;
            if (drive > 'Z') {
                throw new Exception("All drive letters taken");
            }
        }
        return drive;
    }
    public static Iterable<String[]> drivesIterator(final String drives) {
        final int[] index = {drives.indexOf(":")};
        final String[] item = new String[2];
        return () -> new Iterator<String[]>() {
            @Override
            public boolean hasNext() {
                return index[0] != -1;
            }

            @Override
            public String[] next() {
                item[0] = String.valueOf(drives.charAt(index[0]-1));
                int nextIndex = drives.indexOf(":", index[0]+1);
                item[1] = drives.substring(index[0]+1, nextIndex != -1 ? nextIndex-1 : drives.length());
                index[0] = nextIndex;
                return item;
            }
        };
    }

    public void saveData() {
        try {
            JSONObject data = new JSONObject();
            data.put("id", id);
            data.put("name", name);
            data.put("screenSize", screenSize);
            data.put("envVars", envVars);
            data.put("cpuList", cpuList);
            data.put("cpuListWoW64", cpuListWoW64);
            data.put("graphicsDriver", graphicsDriver);
            data.put("graphicsDriverVersion", graphicsDriverVersion); // Ensure this is added
            if (!graphicsDriverConfig.isEmpty()) data.put("graphicsDriverConfig", graphicsDriverConfig);
            data.put("dxwrapper", dxwrapper);
            if (!dxwrapperConfig.isEmpty()) data.put("dxwrapperConfig", dxwrapperConfig);
            data.put("audioDriver", audioDriver);
            data.put("wincomponents", wincomponents);
            data.put("drives", drives);
            data.put("showFPS", showFPS);
            data.put("launchRealSteam", launchRealSteam);
            data.put("allowSteamUpdates", allowSteamUpdates);
            data.put("inputType", inputType);
            data.put("dinputMapperType", dinputMapperType);
            data.put("wow64Mode", wow64Mode);
            data.put("startupSelection", startupSelection);
            data.put("box86Version", box86Version);
            data.put("box64Version", box64Version);
            data.put("box86Preset", box86Preset);
            data.put("box64Preset", box64Preset);
            data.put("fexcorePreset", fexcorePreset);
            data.put("desktopTheme", desktopTheme);
            data.put("extraData", extraData);
            data.put("sessionMetadata", sessionMetadata);
            data.put("rcfileId", rcfileId);
            data.put("midiSoundFont", midiSoundFont);
            data.put("lc_all", lc_all);
            data.put("primaryController", primaryController);
            data.put("controllerMapping", controllerMapping);
            data.put("execArgs", execArgs);
            data.put("executablePath", executablePath);
            data.put("needsUnpacking", needsUnpacking);
            data.put("sdlControllerAPI", sdlControllerAPI);
            // Disable mouse input flag
            data.put("disableMouseInput", disableMouseInput);
            // Touchscreen mode flag
            data.put("touchscreenMode", touchscreenMode);
            data.put("useDRI3", useDRI3);
            data.put("installPath", installPath);
            data.put("steamType", steamType);
            data.put("language", language);
            data.put("containerVariant", containerVariant);
            data.put("emulator", emulator);
            data.put("fexcoreVersion", fexcoreVersion);

            // Emulated keyboard/mouse controller mappings
            data.put("emulateKeyboardMouse", emulateKeyboardMouse);
            if (controllerEmulationBindings != null) {
                data.put("controllerEmulationBindings", controllerEmulationBindings);
            }

            // Force DLC setting
            data.put("forceDlc", forceDlc);

            // Use Legacy DRM setting
            data.put("useLegacyDRM", useLegacyDRM);

            if (!WineInfo.isMainWineVersion(wineVersion)) data.put("wineVersion", wineVersion);
            FileUtils.writeString(getConfigFile(), data.toString());
        }
        catch (JSONException e) {
            Log.e("Container", "Failed to save data: " + e);
        }
    }

    public void loadData(JSONObject data) throws JSONException {
        wineVersion = WineInfo.MAIN_WINE_VERSION.identifier();
        dxwrapperConfig = "";
        checkObsoleteOrMissingProperties(data);

        for (Iterator<String> it = data.keys(); it.hasNext(); ) {
            String key = it.next();
            switch (key) {
                case "name" :
                    setName(data.getString(key));
                    break;
                case "screenSize" :
                    setScreenSize(data.getString(key));
                    break;
                case "envVars" :
                    setEnvVars(data.getString(key));
                    break;
                case "cpuList" :
                    setCPUList(data.getString(key));
                    break;
                case "cpuListWoW64" :
                    setCPUListWoW64(data.getString(key));
                    break;
                case "graphicsDriver" :
                    setGraphicsDriver(data.getString(key));
                    break;
                case "graphicsDriverVersion":
                    setGraphicsDriverVersion(data.getString(key));
                    break;
                case "graphicsDriverConfig" :
                    setGraphicsDriverConfig(data.getString(key));
                    break;
                case "wincomponents" :
                    setWinComponents(data.getString(key));
                    break;
                case "dxwrapper" :
                    setDXWrapper(data.getString(key));
                    break;
                case "dxwrapperConfig" :
                    setDXWrapperConfig(data.getString(key));
                    break;
                case "drives" :
                    setDrives(data.getString(key));
                    break;
                case "showFPS" :
                    setShowFPS(data.getBoolean(key));
                    break;
                case "launchRealSteam" :
                    setLaunchRealSteam(data.getBoolean(key));
                    break;
                case "allowSteamUpdates" :
                    setAllowSteamUpdates(data.getBoolean(key));
                    break;
                case "steamType" :
                    setSteamType(data.getString(key));
                    break;
                case "language" :
                    setLanguage(data.getString(key));
                    break;
                case "containerVariant" :
                    setContainerVariant(data.getString(key));
                    break;
                case "inputType" :
                    setInputType(data.getInt(key));
                    break;
                case "dinputMapperType" :
                    setDinputMapperType((byte) data.getInt(key));
                    break;
                case "wow64Mode" :
                    setWoW64Mode(data.getBoolean(key));
                    break;
                case "startupSelection" :
                    setStartupSelection((byte)data.getInt(key));
                    break;
                case "extraData" : {
                    JSONObject extraData = data.getJSONObject(key);
                    setExtraData(extraData);
                    break;
                }
                case "sessionMetadata" : {
                    try {
                        JSONObject sessionMetadata = data.getJSONObject(key);
                        this.sessionMetadata = sessionMetadata;
                    } catch (JSONException e) {
                        this.sessionMetadata = null;
                    }
                    break;
                }
                case "wineVersion" :
                    setWineVersion(data.getString(key));
                    break;
                case "emulator" :
                    setEmulator(data.getString(key));
                    break;
                case "box86Version":
                    setBox86Version(data.getString(key));
                    break;
                case "box64Version":
                    setBox64Version(data.getString(key));
                    break;
                case "box86Preset" :
                    setBox86Preset(data.getString(key));
                    break;
                case "box64Preset" :
                    setBox64Preset(data.getString(key));
                    break;
                case "fexcorePreset":
                    setFEXCorePreset(data.getString(key));
                    break;
                case "audioDriver" :
                    setAudioDriver(data.getString(key));
                    break;
                case "desktopTheme" :
                    setDesktopTheme(data.getString(key));
                    break;
                case "rcfileId" :
                    setRcfileId(data.getInt(key));
                    break;
                case "midiSoundFont" :
                    setMidiSoundFont(data.getString(key));
                    break;
                case "lc_all" :
                    setLC_ALL(data.getString(key));
                    break;
                case "primaryController" :
                    setPrimaryController(data.getInt(key));
                    break;
                case "controllerMapping" :
                    controllerMapping = data.getString(key);
                    break;
                case "execArgs" :
                    setExecArgs(data.getString(key));
                    break;
                case "executablePath" :
                    setExecutablePath(data.getString(key));
                    break;
                case "needsUnpacking" :
                    setNeedsUnpacking(data.getBoolean(key));
                    break;
                case "sdlControllerAPI" :
                    setSdlControllerAPI(data.getBoolean(key));
                    break;
                case "disableMouseInput" :
                    setDisableMouseInput(data.getBoolean(key));
                    break;
                case "touchscreenMode" :
                    setTouchscreenMode(data.getBoolean(key));
                    break;
                case "useDRI3" :
                    setUseDRI3(data.getBoolean(key));
                    break;
                case "fexcoreVersion" :
                    setFEXCoreVersion(data.getString(key));
                    break;
                case "installPath":
                    setInstallPath(data.getString(key));
                    break;
                case "emulateKeyboardMouse":
                    this.emulateKeyboardMouse = data.getBoolean(key);
                    break;
                case "controllerEmulationBindings":
                    this.controllerEmulationBindings = data.getJSONObject(key);
                    break;
                case "forceDlc":
                    this.forceDlc = data.getBoolean(key);
                    break;
                case "useLegacyDRM":
                    this.useLegacyDRM = data.getBoolean(key);
                    break;
            }
        }
    }

    public static void checkObsoleteOrMissingProperties(JSONObject data) {
        try {
            if (data.has("dxcomponents")) {
                data.put("wincomponents", data.getString("dxcomponents"));
                data.remove("dxcomponents");
            }

            if (data.has("dxwrapper")) {
                String dxwrapper = data.getString("dxwrapper");
                if (dxwrapper.equals("original-wined3d")) {
                    data.put("dxwrapper", DEFAULT_DXWRAPPER);
                }
                else if (dxwrapper.startsWith("d8vk-") || dxwrapper.startsWith("dxvk-")) {
                    data.put("dxwrapper", dxwrapper);
                }
            }

            if (data.has("graphicsDriver")) {
                String graphicsDriver = data.getString("graphicsDriver");
                if (graphicsDriver.equals("turnip-zink")) {
                    data.put("graphicsDriver", "turnip");
                }
                else if (graphicsDriver.equals("llvmpipe")) {
                    data.put("graphicsDriver", "virgl");
                }
            }

            KeyValueSet wincomponents1 = new KeyValueSet(DEFAULT_WINCOMPONENTS);
            KeyValueSet wincomponents2 = new KeyValueSet(data.getString("wincomponents"));
            String result = "";

            for (String[] wincomponent1 : wincomponents1) {
                String value = wincomponent1[1];

                for (String[] wincomponent2 : wincomponents2) {
                    if (wincomponent1[0].equals(wincomponent2[0])) {
                        value = wincomponent2[1];
                        break;
                    }
                }

                result += (!result.isEmpty() ? "," : "")+wincomponent1[0]+"="+value;
            }

            data.put("wincomponents", result);

            // Initialize defaults for new emulate keyboard/mouse fields if missing
            if (!data.has("emulateKeyboardMouse")) {
                data.put("emulateKeyboardMouse", false);
            }
            if (!data.has("controllerEmulationBindings")) {
                JSONObject defaults = new JSONObject();
                // Defaults per request
                defaults.put("L2", "MOUSE_LEFT_BUTTON");
                defaults.put("R2", "MOUSE_RIGHT_BUTTON");
                defaults.put("A", "KEY_SPACE");
                defaults.put("B", "KEY_E");
                defaults.put("X", "KEY_Q");
                defaults.put("Y", "KEY_TAB");
                defaults.put("SELECT", "KEY_ESC");
                // All others default to NONE
                defaults.put("L1", "KEY_SHIFT_L");
                defaults.put("L3", "NONE");
                defaults.put("R1", "KEY_CTRL_R");
                defaults.put("R3", "NONE");
                defaults.put("DPAD_UP", "KEY_UP");
                defaults.put("DPAD_DOWN", "KEY_DOWN");
                defaults.put("DPAD_LEFT", "KEY_LEFT");
                defaults.put("DPAD_RIGHT", "KEY_RIGHT");
                defaults.put("START", "KEY_ENTER");
                data.put("controllerEmulationBindings", defaults);
            }
        }
        catch (JSONException e) {
            Log.e("Container", "Failed to check obsolete or missing properties: " + e);
        }
    }

    public boolean isEmulateKeyboardMouse() {
        return emulateKeyboardMouse;
    }

    public void setEmulateKeyboardMouse(boolean emulate) {
        this.emulateKeyboardMouse = emulate;
    }

    public boolean isForceDlc() {
        return forceDlc;
    }

    public void setForceDlc(boolean forceDlc) {
        this.forceDlc = forceDlc;
    }

    public boolean isUseLegacyDRM() {
        return useLegacyDRM;
    }

    public void setUseLegacyDRM(boolean useLegacyDRM) {
        this.useLegacyDRM = useLegacyDRM;
    }

    public JSONObject getControllerEmulationBindings() {
        return controllerEmulationBindings;
    }

    public void setControllerEmulationBindings(JSONObject bindings) {
        this.controllerEmulationBindings = bindings;
    }

    public String getContainerJson() {
        String content = FileUtils.readString(getConfigFile());
        if (content == null) {
            Log.e("Container", "Failed to read container config file");
            return "{}";
            }
        return content.replace("\\u0000", "").replace("\u0000", "");
    }

    public static String getFallbackCPUList() {
        String cpuList = "";
        int numProcessors = Runtime.getRuntime().availableProcessors();
        for (int i = 0; i < numProcessors; i++) cpuList += (!cpuList.isEmpty() ? "," : "")+i;
        return cpuList;
    }

    public static String getFallbackCPUListWoW64() {
        String cpuList = "";
        int numProcessors = Runtime.getRuntime().availableProcessors();
        for (int i = numProcessors / 2; i < numProcessors; i++) cpuList += (!cpuList.isEmpty() ? "," : "")+i;
        return cpuList;
    }

    // Disable external mouse input
    public boolean isDisableMouseInput() {
        return disableMouseInput;
    }

    public void setDisableMouseInput(boolean disableMouseInput) {
        this.disableMouseInput = disableMouseInput;
    }

    // Touchscreen mode
    public boolean isTouchscreenMode() {
        return touchscreenMode;
    }

    public void setTouchscreenMode(boolean touchscreenMode) {
        this.touchscreenMode = touchscreenMode;
    }

    // Use DRI3 WSI
    public boolean isUseDRI3() {
        return useDRI3;
    }

    public void setUseDRI3(boolean useDRI3) {
        this.useDRI3 = useDRI3;
    }
}
